package com.itjiange.service.judge.service.impl;

import cn.hutool.json.JSONUtil;
import com.itjiange.common.common.ErrorCode;
import com.itjiange.common.exception.BusinessException;
import com.itjiange.model.codesandbox.ExecuteCodeRequest;
import com.itjiange.model.codesandbox.ExecuteCodeResponse;
import com.itjiange.model.codesandbox.JudgeContext;
import com.itjiange.model.codesandbox.JudgeInfo;
import com.itjiange.model.dto.question.JudgeCase;
import com.itjiange.model.entity.Question;
import com.itjiange.model.entity.QuestionSubmit;
import com.itjiange.model.enums.QuestionSubmitStatusEnum;
import com.itjiange.service.client.QuestionFeignClient;
import com.itjiange.service.judge.codesandbox.CodeSandBox;
import com.itjiange.service.judge.codesandbox.CodeSandBoxFactory;
import com.itjiange.service.judge.codesandbox.CodeSandBoxProxy;
import com.itjiange.service.judge.manager.JudgeManager;
import com.itjiange.service.judge.service.JudgeService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author ItJianGe
 * @data 2024/3/25 10:51
 */
@Service
public class JudgeServiceImpl implements JudgeService {

    @Resource
    private QuestionFeignClient questionFeignClient;

    @Resource
    private JudgeManager judgeManager;

    @Value("${codesandbox.type:example}")
    private String type;

    @Override
    public QuestionSubmit doQuestion(long questionSubmitId) {
        // 1. 根据提交 id 获取题目提交信息，方便校验
        QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
        if (questionSubmit == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息失败");
        }
        // 1.1. 判断题目是否存在
        Long questionId = questionSubmit.getQuestionId();
        Question question = questionFeignClient.getQuestionById(questionId);
        if (question == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目信息不存在");
        }
        // 1.2. 判断题目状态，是否需要进行判题操作
        Integer status = questionSubmit.getStatus();
        // 1.3. 如果判题状态是正在等待中，则无需进行重复判题
        if (status.equals(QuestionSubmitStatusEnum.RUNNING.getValue())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目正在判题中");
        }
        // 2. 更改题目提交信息
        // 2.1. 更改判题状态为”判题中“
        questionSubmit.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
        boolean updateFlag = questionFeignClient.updateById(questionSubmit);
        if (!updateFlag) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新数据失败");
        }
        // 3. 选择代码沙箱，调用代码沙箱
        CodeSandBox codeSandBox = CodeSandBoxFactory.getCodeSandBox(type);
        CodeSandBoxProxy codeSandBoxProxy = new CodeSandBoxProxy(codeSandBox);
        // 3.1. 获取输入题目信息
        String judgeCaseStr = question.getJudgeCase();
        List<JudgeCase> JudgeCaselist = JSONUtil.toList(judgeCaseStr, JudgeCase.class);
        List<String> inputList = JudgeCaselist.stream().map(JudgeCase::getInput).collect(Collectors.toList());
        String code = questionSubmit.getCode();
        String language = questionSubmit.getLanguage();
        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .input(inputList)
                .code(code)
                .language(language)
                .build();
        // 4. 执行代码并返回结果
        ExecuteCodeResponse executeCodeResponse = codeSandBoxProxy.executeCode(executeCodeRequest);

        // 5. 根据代码沙箱的返回结果进行判断
        JudgeContext judgeContext = new JudgeContext();
        judgeContext.setJudgeInfo(executeCodeResponse.getJudgeInfo());
        judgeContext.setInputList(inputList);
        judgeContext.setOutputList(executeCodeResponse.getOutput());
        judgeContext.setJudgeCaseList(JudgeCaselist);
        judgeContext.setQuestion(question);
        judgeContext.setQuestionSubmit(questionSubmit);
        JudgeInfo judgeInfo = judgeManager.doJudge(judgeContext);

        // 6. 修改数据库的判题结果
        questionSubmit.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());
        questionSubmit.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
        boolean flag = questionFeignClient.updateById(questionSubmit);
        if (!flag) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新失败");
        }
        return questionSubmit;
    }
}
